﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Atosenet
{
    public static class Converter
    {
        public static byte[] StructToBytes(object obj)
        {
            int rawsize = Marshal.SizeOf(obj);
            IntPtr buffer = Marshal.AllocHGlobal(rawsize);
            Marshal.StructureToPtr(obj, buffer, false);
            byte[] rawdatas = new byte[rawsize];
            Marshal.Copy(buffer, rawdatas, 0, rawsize);
            Marshal.FreeHGlobal(buffer);
            return rawdatas;
        }

        public static object BytesToStruct(byte[] buf, int len, Type type)
        {
            object rtn;
            IntPtr buffer = Marshal.AllocHGlobal(len);
            Marshal.Copy(buf, 0, buffer, len);
            rtn = Marshal.PtrToStructure(buffer, type);
            Marshal.FreeHGlobal(buffer);
            return rtn;
        }

        public static void BytesToStruct(byte[] buf, int len, object rtn)
        {
            IntPtr buffer = Marshal.AllocHGlobal(len);
            Marshal.Copy(buf, 0, buffer, len);
            Marshal.PtrToStructure(buffer, rtn);
            Marshal.FreeHGlobal(buffer);
        }

        public static void BytesToStruct(byte[] buf, object rtn)
        {
            BytesToStruct(buf, buf.Length, rtn);
        }

        public static object BytesToStruct(byte[] buf, Type type)
        {
            return BytesToStruct(buf, buf.Length, type);
        }  

        public static string ConvertToHexString(byte[] data)
        {
            if (data == null || data.Length == 0)
                return string.Empty;

            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("X2"));
            }
            return sb.ToString();
        }

        public static string ConvertToDataString(byte[] data, string encodingName)
        {
            if (data == null || data.Length == 0)
                return string.Empty;

            try
            {
                return Encoding.GetEncoding(encodingName).GetString(data);
            }
            catch 
            {
                return string.Empty;
            }
        }

        public static string ConvertToDataString(byte[] data, int index, int length, string encodingName)
        {
            if (data == null || data.Length == 0 || index + length > data.Length)
                return string.Empty;

            try
            {
                return Encoding.GetEncoding(encodingName).GetString(data, index, length);
            }
            catch 
            {
                return string.Empty;
            }
        }

        public static string ConvertToDataString(byte[] data, int index, int length)
        {
            return ConvertToDataString(data, index, length, "us-ascii");
        }

        public static string ConvertToDataString(byte[] data)
        {
            return ConvertToDataString(data, "us-ascii");
        }

        public static byte[] ConvertToByteArray(string hexStr)
        {
            if (string.IsNullOrEmpty(hexStr))
                return new byte[0];

            hexStr = hexStr.Replace(" ", string.Empty);
            List<byte> result = new List<byte>();

            for (int i = 0; i < hexStr.Length; i += 2)
            {
                byte b;

                if (i + 2 > hexStr.Length)
                {
                    if (byte.TryParse(hexStr.Substring(i, 1), System.Globalization.NumberStyles.HexNumber, null, out b))
                        result.Add(b);
                    else
                        continue;
                }
                else
                {
                    if (byte.TryParse(hexStr.Substring(i, 2), System.Globalization.NumberStyles.HexNumber, null, out b))
                        result.Add(b);
                    else
                        continue;
                }
            }
            return result.ToArray();
        }

        public static byte[] ConvertToByteArray(string dataStr, string encodingName)
        {
            if (string.IsNullOrEmpty(dataStr))
                return new byte[0];

            try
            {
                return Encoding.GetEncoding(encodingName).GetBytes(dataStr);
            }
            catch 
            {
                return new byte[0];
            }
        }

        public static byte[] ConvertToByteArray(char[] cArray, string encodingName)
        {
            if (cArray == null || cArray.Length == 0)
                return new byte[0];

            try
            {
                return Encoding.GetEncoding(encodingName).GetBytes(cArray);
            }
            catch 
            {
                return new byte[0];
            }
        }

        public static byte[] ConvertToByteArray(char[] cArray)
        {
            return ConvertToByteArray(cArray, "us-ascii");
        }

        public static UInt16 ConvertToUInt16BE(byte[] data)
        {
            if (data == null || data.Length != 2)
                return 0;

            return ConvertToUInt16BE(data, 0);
        }

        public static UInt16 ConvertToUInt16BE(byte[] data, int offset)
        {
            if (data == null || offset + 1 >= data.Length)
                return 0;

            return (UInt16)(data[offset] << 8 | data[offset + 1]);
        }

        public static byte[] ConvertToByteArrayBE(UInt16 value)
        {
            byte[] data = new byte[2];
            data[0] = (byte)(value >> 8);
            data[1] = (byte)(value & 0xFF);
            return data;
        }

        public static void ConvertToByteArrayBE(UInt16 value, ref byte[] data, int offset)
        {
            if (data == null || offset + 1 >= data.Length)
                return;

            data[offset] = (byte)(value >> 8);
            data[offset + 1] = (byte)(value & 0xFF);
        }

        public static UInt16 ConvertToUInt16LE(byte[] data)
        {
            if (data == null || data.Length != 2)
                return 0;

            return ConvertToUInt16LE(data, 0);
        }

        public static UInt16 ConvertToUInt16LE(byte[] data, int offset)
        {
            if (data == null || offset + 1 >= data.Length)
                return 0;

            return (UInt16)(data[offset] | data[offset + 1] << 8);
        }

        public static byte[] ConvertToByteArrayLE(UInt16 value)
        {
            byte[] data = new byte[2];
            data[0] = (byte)(value & 0xFF);
            data[1] = (byte)(value >> 8);
            return data;
        }

        public static void ConvertToByteArrayLE(UInt16 value, ref byte[] data, int offset)
        {
            if (data == null || offset + 1 >= data.Length)
                return;

            data[offset] = (byte)(value & 0xFF);
            data[offset + 1] = (byte)(value >> 8);
        }

        public static UInt32 ConvertToUInt32BE(byte[] data)
        {
            if (data == null || data.Length != 4)
                return 0;

            return ConvertToUInt32BE(data, 0);
        }

        public static UInt32 ConvertToUInt32BE(byte[] data, int offset)
        {
            if (data == null || offset + 3 >= data.Length)
                return 0;

            return (UInt32)((data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | (data[offset + 3]));
        }

        public static byte[] ConvertToByteArrayBE(UInt32 value)
        {
            byte[] data = new byte[4];
            data[0] = (byte)(value >> 24);
            data[1] = (byte)(value >> 16);
            data[2] = (byte)(value >> 8);
            data[3] = (byte)(value & 0xFF);
            return data;
        }

        public static void ConvertToByteArrayBE(UInt32 value, ref byte[] data, int offset)
        {
            if (data == null || offset + 3 >= data.Length)
                return;

            data[offset] = (byte)(value >> 24);
            data[offset + 1] = (byte)(value >> 16);
            data[offset + 2] = (byte)(value >> 8);
            data[offset + 3] = (byte)(value & 0xFF);
        }

        public static UInt32 ConvertToUInt32LE(byte[] data)
        {
            if (data == null || data.Length != 4)
                return 0;

            return ConvertToUInt32LE(data, 0);
        }

        public static UInt32 ConvertToUInt32LE(byte[] data, int offset)
        {
            if (data == null || offset + 3 >= data.Length)
                return 0;

            return (UInt32)(data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24);
        }

        public static byte[] ConvertToByteArrayLE(UInt32 value)
        {
            byte[] data = new byte[4];
            data[0] = (byte)(value & 0xFF);
            data[1] = (byte)(value >> 8);
            data[2] = (byte)(value >> 16);
            data[3] = (byte)(value >> 24);
            return data;
        }

        public static void ConvertToByteArrayLE(UInt32 value, ref byte[] data, int offset)
        {
            if (data == null || offset + 3 >= data.Length)
                return;

            data[offset] = (byte)(value & 0xFF);
            data[offset + 1] = (byte)(value >> 8);
            data[offset + 2] = (byte)(value >> 16);
            data[offset + 3] = (byte)(value >> 24);
        }

        public static unsafe Single ConvertToSingleBE(byte[] data)
        {
            if (data == null || data.Length != 4)
                return 0f;

            UInt32 value = ConvertToUInt32BE(data);
            return *((Single*)&value);
        }

        public static unsafe Single ConvertToSingleBE(byte[] data, int offset)
        {
            if (data == null || offset + 4 > data.Length)
                return 0f;

            UInt32 value = ConvertToUInt32BE(data, offset);
            return *((Single*)&value);
        }

        public static unsafe Single ConvertToSingleLE(byte[] data, int offset)
        {
            if (data == null || offset + 4 > data.Length)
                return 0f;

            UInt32 value = ConvertToUInt32LE(data, offset);
            return *((Single*)&value);
        }

        public static unsafe Single ConvertToSingleLE(byte[] data)
        {
            if (data == null || data.Length != 4)
                return 0f;

            UInt32 value = ConvertToUInt32LE(data);
            return *((Single*)&value);
        }

        public static unsafe byte[] ConvertToByteArrayLE(Single value)
        {
            UInt32 tValue = *((UInt32*)&value);
            return ConvertToByteArrayLE(tValue);
        }

        public static unsafe void ConvertToByteArrayLE(Single value, ref byte[] data, int offset)
        {
            if (data == null || offset + 3 >= data.Length)
                return;

            UInt32 tValue = *((UInt32*)&value);
            ConvertToByteArrayLE(tValue, ref data, offset);
        }

        public static unsafe byte[] ConvertToByteArrayBE(Single value)
        {
            UInt32 tValue = *((UInt32*)&value);
            return ConvertToByteArrayBE(tValue);
        }

        public static unsafe void ConvertToByteArrayBE(Single value, ref byte[] data, int offset)
        {
            if (data == null || offset + 3 >= data.Length)
                return;

            UInt32 tValue = *((UInt32*)&value);
            ConvertToByteArrayBE(tValue, ref data, offset);
        }
    }
}
